<?php
/**
 * Nordic Repository
 * 
 * LICENSE
 * 
 * The new BSD license is applied on this source-file. For further
 * information please visit http://license.nordic-dev.de/newbsd.txt
 * or send an email to andre.moelle@gmail.com.
 */

/**
 * Necessary includes.
 */
//Zend_Loader::loadClass('Zend_Controller_Action_Helper_ViewRenderer');
Zend_Loader::loadClass('Zend_Controller_Action_Helper_Abstract');
Zend_Loader::loadClass('Nordic_Pagebuilder_Exception');
Zend_Loader::loadClass('Nordic_Pagebuilder_DecoratorManager');
Zend_Loader::loadClass('Nordic_Pagebuilder_Stylesheet');
Zend_Loader::loadClass('Zend_Session_Namespace');

// @TODO: try to replace these
Zend_Loader::loadClass('Nordic_Schema');
Zend_Loader::loadClass('Nordic_Config_Php');
Zend_Loader::loadClass('Nordic_View_Factory');

/**
 * The Pagebuilder provides some methods for creating an easy
 * extensible skeleton of the website.
 * 
 * @category   Nordic
 * @package    Nordic_Pagebuilder
 * @copyright  2007 Nordic Development
 * @license    http://license.nordic-dev.de/newbsd.txt (New-BSD license)
 * @author     Andre Moelle <andre.moelle@gmail.com>
 * @version    $Id: Pagebuilder.php 3 2007-07-08 09:21:42Z andre.moelle $
 */
//class Nordic_Pagebuilder extends Zend_Controller_Action_Helper_ViewRenderer
class Nordic_Pagebuilder extends Zend_Controller_Action_Helper_Abstract
{
	/**
	 * Instance of a config-object.
	 * 
	 * Following options are possible:
	 * 
	 * author - author of the page (used in HTML-header)
	 * css - information about css-files, e.g.
	 *       "css-file:media-type,css-file2:screen" (will load 
	 *       "css-file.css" and "css-file2.css" with their assigned
	 *       media-types)
	 * description - little text about the page (used in HTML-header)
	 * favicon - favicon for the page
	 * footer - name of the view-script for the footer-part of the page
	 * header - name of the view-script for the header-part of the page
	 * javascript - default javascript-files, e.g.
	 *              "foo,bar" (will load "foo.js" and "bar.js")
	 * pretitle - title which is prepended to each page
	 * keywords - meta-keywords
	 * title - default title of the page
	 * 
	 * @var Zend_Config
	 */
	private $_config;
	
	/**
	 * The css-files which should be loaded.
	 * 
	 * @var Array
	 */
	private $_css = array();
	
	/**
	 * The meta-description.
	 * 
	 * @param string
	 */
	private $_description = '';
	
	/**
	 * Error-messages.
	 * 
	 * @var array
	 */
	private $_errors = array();
	
	/**
	 * Instance of the pagebuilder.
	 * 
	 * @var Nordic_Pagebuilder
	 */
	private static $_instance = null;
	
	/**
	 * Javascript-files which should be loaded.
	 * 
	 * @var array
	 */
	private $_js = array();
	
	/**
	 * Keywords in the meta "keywords".
	 * 
	 * @var array
	 */
	private $_keywords = array();
	
	/**
	 * Notices.
	 * 
	 * @var array
	 */
	private $_notices = array();
	
	/**
	 * Title of the website.
	 * 
	 * This title is appended to the pretitle which can be
	 * configured in the "pagebuilder.php" config-file.
	 * 
	 * @var string
	 */
	private $_title = '';
	
	/**
	 * Instance of a view-object.
	 * 
	 * @var Zend_View_Interface
	 */
	private $_view = null;
	
	/**
	 * Appends messages from a specified namespace.
	 * 
	 * After adding the messages, the namespace is emptied and
	 * removed.
	 * 
	 * @param Zend_Session_Namespace $ns namespace where the messages reside
	 * @return void
	 */
	private function _appendMessages (Zend_Session_Namespace $ns)
	{
		if(!isset($ns->notices) && !isset($ns->errors))
		{
			return;
		}
		
		if(!empty($ns->notices))
		{
			$this->appendNotice($ns->notices);
			$ns->notices = array();
		}
		if(!empty($ns->errors))
		{
			$this->appendError($ns->errors);
			$ns->errors = array();
		}
		
		$ns->unsetAll();
		$ns->setExpirationSeconds(1);
	}
	
	/**
	 * Returns new stylesheet-object.
	 * 
	 * @param string $name name of the stylesheet
	 * @param string $media media-type
	 * @return Nordic_Pagebuilder_Stylesheet
	 */
	private function _getStylesheet ($name, $media)
	{
		Zend_Loader::loadClass('Nordic_Pagebuilder_Stylesheet');
		
		return new Nordic_Pagebuilder_Stylesheet($name, $media);
	}
	
	/**
	 * Returns the view-object.
	 * 
	 * @param void
	 * @return Zend_View_Interface
	 */
	private function _getView ()
	{
		if($this->_view == null)
		{
			$this->_view = Nordic_View_Factory::getView();
		}
		
		return $this->_view;
	}
	
	/**
	 * Parses the config-string of stylesheets.
	 * 
	 * Result is the number of added stylesheets.
	 * 
	 * @param string $string information about the stylesheets
	 * @return integer
	 */
	private function _parseStylesheets ($string)
	{
		$result = 0;
		
		foreach(explode("\n", $string) as $stylesheet)
		{
			if(!$stylesheet)
			{
				continue;
			}
			
			$manager = Nordic_Pagebuilder_DecoratorManager::getInstance();
			
			if(($count = substr_count($stylesheet, "\t")) !== false)
			{
				if($count == 1)
				{
					list($sheet, $media) = explode("\t", $stylesheet);
					$cc = null;
				}
				else if($count >= 2)
				{
					list($sheet, $media, $cc) = explode("\t", $stylesheet, 3);
					
					$array = explode(' ', $cc);
					
					$comp = $array[0];
					
					if(isset($array[1]))
					{
						$version = $array[1];
					}
					else
					{
						$version = '';
					}
				}
			}
			else
			{
				$sheet = $stylesheet;
				$media = null;
				$cc = null;
			}
			
			$this->addStylesheet(
				$this->_getStylesheet($sheet, $media),
				($cc == null ? null : 
					$manager->getDecorator(
						'ConditionalComment',
						array('version' => $version, 'comparator' => $comp)
					)
				)
			);
			$result++;
		}
		
		return $result;
	}
	
	/**
	 * This method adds one or more keywords to the keywords-array.
	 * 
	 * Result is the number of keywords added.
	 * 
	 * <code>
	 * $this->addKeyword('foo');
	 * $this->addKeyword('bar');
	 * 
	 * # following will create the same result
	 * $this->addKeyword(array('foo', 'bar'));
	 * </code>
	 * 
	 * @param string/array $keyword keyword to add
	 * @return integer
	 */
	public function addKeyword ($keyword)
	{
		if(!$keyword)
		{
			return 0;
		}
		
		if(!is_array($keyword))
		{
			$keyword = strtolower(trim($keyword));
			
			if(strpos($keyword, ' ') === false)
			{
				$this->_keywords[] = $keyword;
				
				return 1;
			}
			
			$keyword = explode(' ', $keyword);
		}
		
		$result = 0;
		
		foreach($keyword as $temp)
		{
			$result += $this->addKeyword($temp);
		}
		
		return $result;
	}
	
	/**
	 * Adds one or more css-stylesheets to the header.
	 * 
	 * The extension of the filename is not required.
	 * 
	 * <code>
	 * $this->addStylesheet('foo');
	 * $this->addStylesheet('bar');
	 * 
	 * # following will create the same result
	 * $this->addStylesheet(array('foo', 'bar'));
	 * </code>
	 * 
	 * @param String/Array $name filename
	 * @return Integer
	 */
	public function addStylesheet ($stylesheet, $decorator = null)
	{
		if($stylesheet instanceof Nordic_Pagebuilder_Stylesheet)
		{
			if($decorator != null && $decorator instanceof Nordic_Pagebuilder_Decorator)
			{
				$decorator->decorate($stylesheet);
				$stylesheet = $decorator;
			}
			
			$this->_css[] = $stylesheet;
			
			return 1;
		}
		
		$result = 0;
		
		foreach($stylesheet as $stylesheet)
		{
			$result += $this->addStylesheet($stylesheet);
		}
		
		return $result;
	}
	
	/**
	 * Appends errors.
	 * 
	 * @param string $message error-message
	 * @return integer
	 */
	public function appendError ($message)
	{
		if(!is_array($message))
		{
			$this->_errors[] = Zend_Registry::get('Translator')->_($message);
			
			return 1;
		}
		
		$result = 0;
		
		foreach($message as $message)
		{
			$result += $this->appendError($message);
		}
		
		return $result;
	}
	
	/**
	 * Appends notices.
	 * 
	 * @param string $message notice
	 */
	public function appendNotice ($message)
	{
		if(!is_array($message))
		{
			$this->_notices[] = Zend_Registry::get('Translator')->_($message);
			
			return 1;
		}
		
		$result = 0;
		
		foreach($message as $message)
		{
			$result += $this->appendNotice($message);
		}
		
		return $result;
	}
	
	/**
	 * Assigns a variable to the pagebuilder-templates.
	 * 
	 * @param string $name name of variable
	 * @param mixed $content content of the variable
	 * @return void
	 */
	public function assign ($name, $content)
	{
		$this->_getView()->assign($name, $content);
	}
	/**
	 * One or more javascript-files are added here.
	 * 
	 * The extension of the file is not required.
	 * 
	 * <code>
	 * $this->addJavaScript('foo');
	 * $this->addJavaScript('bar');
	 * 
	 * # following will create the same result
	 * $this->addJavaScript(array('foo', 'bar'));
	 * </code>
	 * 
	 * @param array/string $name filename
	 * @return integer
	 */
	public function addJavaScript ($name)
	{
		if(!is_array($name))
		{
			if(!$name)
			{
				return 0;
			}
			
			if(Nordic_Util::getExtension($name) != 'js')
			{
				$name .= '.js';
			}
			
			$this->_js[] = $name;
			
			return 1;
		}
		
		$result = 0;
		
		foreach($name as $script)
		{
			$result += $this->addJavaScript($script);
		}
		
		return $result;
	}
	
	/**
	 * Returns the author of the page.
	 * 
	 * @param void
	 * @return String
	 */
	public function getAuthor ()
	{
		return $this->_config->author;
	}
	
	/**
	 * Returns the description of the page.
	 * 
	 * @param void
	 * @return String
	 */
	public function getDescription ()
	{
		return $this->_description;
	}
	
	/**
	 * Returns the errors.
	 * 
	 * @param void
	 * @return array
	 */
	public function getErrors ()
	{
		return $this->_errors;
	}
	
	/**
	 * Returns the favicon-filename.
	 * 
	 * @param void
	 * @return String
	 */
	public function getFavicon ()
	{
		return $this->_config->favicon;
	}
	
	/**
	 * Returns the instance using the helper-broker.
	 * 
	 * @return Nordic_Pagebuilder
	 */
	public static function getInstance ()
	{
		Zend_Loader::loadClass('Zend_Controller_Action_HelperBroker');
		
		return Zend_Controller_Action_HelperBroker::getExistingHelper('Pagebuilder');
	}
	
	/**
	 * Returns the keywords.
	 * 
	 * @param void
	 * @return Array
	 */
	public function getKeywords ()
	{
		return implode(' ', array_unique($this->_keywords));
	}
	
	/**
	 * Returns the notices.
	 * 
	 * @param void
	 * @return array
	 */
	public function getNotices ()
	{
		return $this->_notices;
	}
	
	/**
	 * Returns the pretitle.
	 * 
	 * The pretitle is the title which is pretended to every title
	 * set.
	 * 
	 * @param void
	 * @return String
	 */
	public function getPretitle ()
	{
		return $this->_config->pretitle;
	}
	
	/**
	 * Returns the scripts.
	 * 
	 * @param void
	 * @return Array
	 */
	public function getScripts ()
	{
		return $this->_js;
	}
	
	/**
	 * Returns the stylesheets.
	 * 
	 * @param void
	 * @return Array
	 */
	public function getStylesheets ()
	{
		return $this->_css;
	}
	
	/**
	 * Returns the title of the page.
	 * 
	 * If no new title was set the default title from the config
	 * will be returned.
	 * 
	 * @param void
	 * @return String
	 */
	public function getTitle ()
	{
		if($this->_title == '')
		{
			return $this->_config->title;
		}
		
		return $this->_title;
	}
	
	public function __construct ()
	{
		$this->_config = Nordic_Config::getInstance()->load('pagebuilder.php');
		$this->addKeyword(explode(',', $this->_config->keywords));
		$this->setDescription($this->_config->description);
		$this->addJavaScript(explode(',', $this->_config->javascript));
		$this->_parseStylesheets($this->_config->css);
		
		$this->_appendMessages(new Zend_Session_Namespace('Reroute'));
		
		$lang = Zend_Registry::get('Application')->language;
		$path = NORDIC_LANG . $lang . '.' . $this->_config->lang;
		
		if(file_exists($path))
		{
			Zend_Registry::get('Translator')->addTranslation(
				Nordic_Schema::encodeFile($path),
				$lang
			);
		}
	}
	
	/**
	 * Renders a page with the default header- and footer-files.
	 * 
	 * @param Zend_Controller_Response_Http $response response-object
	 * @param array $content Additional variables
	 * @return void
	 */
	public function render (Zend_Controller_Response_Http $response, $content = array())
	{
		$view = $this->_getView();
		
		foreach(get_class_methods($this) as $method)
		{
			if(substr($method, 0, 3) != 'get' || $method == 'getInstance')
			{
				continue;
			}
			
			$view->{strtolower(substr($method, 3))} = $this->$method();
		}
		
		foreach($content as $key => $value)
		{
			$view->$key = $value;
		}
		
		$result  = $view->render($this->_config->header);
		$result .= (string)$response;
		$result .= $view->render($this->_config->footer);
		
		return $result;
	}
	
	/**
	 * Sets the description of the page.
	 * 
	 * @param String $description new description
	 * @return void
	 */
	public function setDescription ($description)
	{
		$this->_description = $description;
	}
	
	/**
	 * This method sets a new title.
	 * 
	 * @param String $title
	 * @return void
	 */
	public function setTitle ($title)
	{
		$this->_title = $title;
	}
}
?>